应用程序与驱动程序6种通信方式
---来源于互联网,交流学习---
下面是方式都是用ReadFile,WirteFile设备控制操作的方法基本上与上面3中相对应!
1、缓冲区方式设备读写:
在创建Device后,须要指定方式为Device的Flags有 DO_BUFFERED_IO!通过应用层Api函数ReadFile,WriteFile,等函数,ntoskrnl.exe创建Irp 后,ReadFile和WriteFile参数的缓冲区就在irp->AssociatedIrp.Systembuffer,同时要求读写的偏移量,和长度都在PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLoca
2、直接方式读写
在创建Device后,须要指定方式为Device的Flags有 DO_DIRECT_IO!通过应用层APi函数ReadFile,WriteFile等函数,ntoskrnl.exe创建的Irp 后,ReadFile和WriteFile参数的缓冲区将被锁住,然后操作系统将这段缓冲区在内核模式地址再次映射一遍,这样应用层的缓冲区和内存层的就指向同一个物理内存!而内核模式用MDL数据结构记录这段内存,这个虚拟内存大小在 MmGetByteCount(pIrp->MdlAddress),首地址在 MmGetMdlVirtualAddress(pIrp->MdlAddress);偏移量为 MmGetMdlByteOffset(pIrp->MdlAddress)(这里的偏移量不是文件读写的偏移量,而是在MDL中的偏移量)然后文件的长度还是stack->Parameters.Read.Length,这个值和 MmGetByteCount(pIrp->MdlAddress)是一样的,要不然就出错了,而真正的读写偏移量还是在 stack->Parameters.Read.ByteOffset!这里无论是读还是写,都要得到MDL在内核模式下的映射,因此还要用 MmGetSystemAddressForMdl
3、其他方式读写
这种方式很少用到,在创建Device后,Flags既不标志 DO_BUFFERED_IO也不标志DO_DIRECT_IO,ReadFile和WriteFile提供的缓冲区内存地址,可以再IRP的 pIrp->UserBuffer字段得到,而长度和偏移量还是在stack->Paameters.Read中,但是用这种方法须要注意的是ReadFile可能把空指针地址或者非法地址传递给驱动程序,因此驱动程序使用用户模式地址钱须要检查是否可读或者可写,可以用 ProbeForWrite或者ProbeForWrite函数和try模块。
这里有个问题困扰着我,就是应用层用GetFileSize函数时用第一种方法处理该Irp时结果是正确的,就是冲pIrp->AssociatedIrp.SystemBuffer;得到 PFILE_STANDARD_INFORMATION结构指针,然后讲该结构的EndOfFile设置为你自己文件的长度,然后结束该 IRP,GetFileSize就能得到正确的文件长度,但是用方法三就是的时候用GetFileSize,我同样用 pIrp->UserBufer得到PFILE_STANDARD_INFORMATION结构指针,然后设置后,在GetFileSize里面去得不到正确的长度!!这个是为什么?还有就是该Irp的CurrentStackLocation-stack,该字段里面的 stack->Parameters.QueryFile.Length指的是什么,显示的都是24,无论文长度为多少,还是用哪种方法都市24,很郁闷!!!
下面是方式都是用IO设备控制操作的方法基本上与上面3中相对应!
对于IO设备控制操作,不知道为什么可以用设置DO_DIRECT_IO后就能对三种方式都适用,而且stack->Parameters.Read.Length/Write.Length 都是对应应用层的DeviceIoControl函数中的第6个参数也就是输出缓冲区!DeviceIoControl函数的输入输出缓冲区长度都再 stack->Parameters.DeviceioControl.InputBufferLength/OutputBufferLength 中
4、缓冲内存IOCTL,在DeviceIoControl函数第二个参数的时候,使用CTL_CODE来产生该常数,其中Method字段设置为METHOD_BUFFERED,在内核模式中输入缓冲区很输出缓冲区都为 pIrp->AssociatedIrp.SystemBuffer,
5、直接方式IOCTL,在DeviceIoControl函数第二个参数的时候,使用CTL_CODE来产生该常数,其中Method字段设置为METHOD_IN_DIRECT/METHOD_OUT_DIRECT(最好使用第一个,因为第一个对所有的打开设备方式都通用)!对于第4中的区别是输入缓冲区还在pIrp->AssocatedIrp.Systembuffer 中,但是输出缓冲区却是pIrp->MdlAddress,因此在内核对输出的写应该写在 MmGetSystemAddressForMdl
6、其他方式IOCTL,在DeviceIoControl函数第二个参数的时候,使用CTL_CODE来产生该常数,其中Method字段设置为MEHTOD_NEITHER,输入缓冲区为 stack->Parameters.DeviceIoControl.Tyep3InputBuffer;输出缓冲区为 pIrp->Userbuffer